package org.hxzon.nioserver.impl;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.nio.channels.SelectionKey;
import java.util.LinkedList;

import org.hxzon.nioserver.BufferFactory;
import org.hxzon.nioserver.ChannelFacade;
import org.hxzon.nioserver.OutputQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class LengthOutputQueueImpl implements OutputQueue {
    private static final Logger logger = LoggerFactory.getLogger(LengthOutputQueueImpl.class);
    //
    private final BufferFactory bufferFactory;
    //
    private final ChannelFacade facade;
    private final LinkedList<ByteBuffer> bufQueue;
    private ByteBuffer bufDataCur = null;

    public LengthOutputQueueImpl(ChannelFacade adapter, BufferFactory bufferFactory) {
        this.facade = adapter;
        this.bufferFactory = bufferFactory;
        bufQueue = new LinkedList<ByteBuffer>();
    }

    public synchronized boolean isEmpty() {
        return (bufDataCur == null) && (bufQueue.size() == 0);
    }

    public synchronized void drainTo(ByteChannel channel) throws IOException {
        logger.trace("drainTo thread");
        if (bufDataCur == null) {
            bufDataCur = bufQueue.poll();
        }
        int count = -1;
        while (bufDataCur != null && count != 0) {//if no next bufDataCur,or channel can't write more,break
            while (bufDataCur.hasRemaining() && count != 0) {//if bufDataCur is ok,or channel can't write more,break
                count = channel.write(bufDataCur);
            }
            if (!bufDataCur.hasRemaining()) {//if bufDataCur is ok,next
                bufDataCur = bufQueue.poll();
            }
        }

    }

    // -- not needed by framework

    public synchronized boolean enqueue(ByteBuffer bufDataNew) {
        bufDataNew.flip();
        //
        ByteBuffer bufForWriteLen = bufferFactory.newBuffer(4);
        bufForWriteLen.putInt(bufDataNew.remaining());
        bufForWriteLen.flip();
        bufQueue.add(bufForWriteLen);
        //
        bufQueue.add(bufDataNew);
        //
        facade.modifyInterestOps(SelectionKey.OP_WRITE, 0);

        return true;
    }

}
